package com.moyq5.permit.center.web;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import com.moyq5.mvc.framework.general.Body;
import com.moyq5.mvc.framework.general.Sort;
import com.moyq5.mvc.framework.general.Sorting;
import com.moyq5.mvc.framework.jackson.result.ListResult;
import com.moyq5.mvc.framework.jackson.result.ObjectResult;
import com.moyq5.mvc.framework.jackson.result.StatusResult.Status;
import com.moyq5.mvc.framework.utils.StringUtils;
import com.moyq5.permit.center.bean.embed.AgentAdminId;
import com.moyq5.permit.center.bean.embed.MerchAdminId;
import com.moyq5.permit.center.bean.embed.ShopAdminId;
import com.moyq5.permit.center.bean.entity.AgentAdmin;
import com.moyq5.permit.center.bean.entity.MerchAdmin;
import com.moyq5.permit.center.bean.entity.ShopAdmin;
import com.moyq5.permit.center.bean.entity.SysAdmin;
import com.moyq5.permit.center.bean.entity.SysAdminRole;
import com.moyq5.permit.center.bean.entity.SysPermit;
import com.moyq5.permit.center.bean.entity.SysPermit_;
import com.moyq5.permit.center.bean.entity.SysRole;
import com.moyq5.permit.center.bean.enums.SysPlatform;
import com.moyq5.permit.center.bean.search.AgentAdmin4Search;
import com.moyq5.permit.center.bean.search.MerchAdmin4Search;
import com.moyq5.permit.center.bean.search.ShopAdmin4Search;
import com.moyq5.permit.center.bean.search.SysAdminRole4Search;
import com.moyq5.permit.center.bean.search.SysPermit4Search;
import com.moyq5.permit.center.bean.search.SysRole4Search;
import com.moyq5.permit.center.feign.client.AgentAdminFeign;
import com.moyq5.permit.center.feign.client.MerchAdminFeign;
import com.moyq5.permit.center.feign.client.ShopAdminFeign;
import com.moyq5.permit.center.feign.client.SysAdminRoleFeign;
import com.moyq5.permit.center.feign.client.SysPermitFeign;
import com.moyq5.permit.center.feign.client.SysRoleFeign;

/**
 * 认证、授权Realm
 * @author Moyq5
 * @date 2019年8月13日
 */
public abstract class ShiroRealm extends AuthorizingRealm {
	
	@Autowired
	private AgentAdminFeign agentAdminFeign;
	@Autowired
	private MerchAdminFeign merchAdminFeign;
	@Autowired
	private ShopAdminFeign shopAdminFeign;
	@Autowired
	private SysAdminRoleFeign adminRoleFeign;
	@Autowired
	private SysRoleFeign roleFeign;
	@Autowired
	private SysPermitFeign permitFeign;
	
	protected abstract SysAdmin doGetAdminInfo(AuthenticationToken token);
	
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		SysAdmin admin = doGetAdminInfo(token);
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo();
		info.setPrincipals(createPrincipals(admin));
		info.setCredentials(token.getCredentials());
		return info;
	}

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		
		Long[] permitIds = getPermitIds(info, principals);
		
		// 获取权限列表
		if (permitIds.length > 0) {
			SysPermit4Search permit4s = new SysPermit4Search();
			permit4s.setPlatform(SysPlatform.AUTH);
			permit4s.setPermitIdIn(permitIds);
			Sorting si = new Sorting();
			List<Sort> sorts = new ArrayList<Sort>();
			sorts.add(new Sort(SysPermit_.PERMIT_ID));
			si.setSorts(sorts);
			ListResult<SysPermit> permitListRst = permitFeign.list(new Body<SysPermit4Search, Sorting>(permit4s, si));
			permitListRst.getData().forEach(permit -> {
				if (!StringUtils.isEmpty(permit.getPermit())) {
					info.addStringPermission(permit.getPermit());
				}
			});
		}
		
		return info;
	}
	
	/**
	 * 权限Ids
	 * @author Moyq5
	 * @date 2019年8月12日
	 * @param info
	 * @param principals
	 * @return
	 */
	private Long[] getPermitIds(SimpleAuthorizationInfo info, PrincipalCollection principals) {
		Long adminId = (Long)principals.getPrimaryPrincipal();
		
		// 获取角色列表
		SysAdmin admin = new SysAdmin();
		admin.setAdminId(adminId);
		SysRole role = new SysRole();
		//role.setPlatform(SysPlatform.AUTH);// 可能是系统默认角色
		SysAdminRole4Search adminRole4s = new SysAdminRole4Search();
		adminRole4s.setAdmin(admin);
		adminRole4s.setRole(role);
		ListResult<SysAdminRole> adminRoleListRst = adminRoleFeign.listWithRole(new Body<SysAdminRole4Search, Sorting>(adminRole4s, new Sorting()));
		
		// 权限Ids
		StringBuffer permitIdSb = new StringBuffer();
		
		adminRoleListRst.getData().forEach(adminRole -> {
			SysRole role_ = adminRole.getRole();
			info.addRole(role_.getCode());
			if (!StringUtils.isEmpty(role_.getPermits())) {
				permitIdSb.append("," + role_.getPermits());
			}
		});
		
		// 角色Ids
		Long[] roleIds = getRoleIds(adminId, principals);
		
		if (roleIds.length > 0) {
			SysRole4Search role4s = new SysRole4Search();
			role4s.setPlatform(SysPlatform.AUTH);
			role4s.setRoleIdIn(roleIds);
			ListResult<SysRole> roleListRst = roleFeign.list(new Body<SysRole4Search, Sorting>(role4s, new Sorting()));
			roleListRst.getData().forEach(role_ -> {
				info.addRole(role_.getCode());
				if (!StringUtils.isEmpty(role_.getPermits())) {
					permitIdSb.append("," + role_.getPermits());
				}
			});
		}
		
		return stringToLongArray(permitIdSb.toString());
	}
	
	/**
	 * 角色Ids
	 * @author Moyq5
	 * @date 2019年8月12日
	 * @param adminId
	 * @param principals
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Long[] getRoleIds(Long adminId, PrincipalCollection principals) {
		StringBuffer roleIdSb = new StringBuffer();
		Collection<Long> coll;
		if (null != (coll = principals.fromRealm("agent-center")) && coll.size() > 0) {
			Long agentId = coll.iterator().next();
			AgentAdminId id = new AgentAdminId();
			id.setAdminId(adminId);
			id.setAgentId(agentId);
			AgentAdmin4Search agentAdmin4s = new AgentAdmin4Search();
			agentAdmin4s.setId(id);
			ObjectResult<AgentAdmin> agentAdminRst = agentAdminFeign.one(new Body<AgentAdmin4Search, Sorting>(agentAdmin4s, new Sorting()));
			if (agentAdminRst.getStatus() != Status.OK) {
				throw new AuthenticationException(agentAdminRst.getMessage());
			}
			AgentAdmin agentAdmin = agentAdminRst.getData();
			roleIdSb.append(agentAdmin.getRoles());
		} else if (null != (coll = principals.fromRealm("merch-center")) && coll.size() > 0) {
			Long merchId = coll.iterator().next();
			MerchAdminId id = new MerchAdminId();
			id.setAdminId(adminId);
			id.setMerchId(merchId);
			MerchAdmin4Search merchAdmin4s = new MerchAdmin4Search();
			merchAdmin4s.setId(id);
			ObjectResult<MerchAdmin> merchAdminRst = merchAdminFeign.one(new Body<MerchAdmin4Search, Sorting>(merchAdmin4s, new Sorting()));
			if (merchAdminRst.getStatus() != Status.OK) {
				throw new AuthenticationException(merchAdminRst.getMessage());
			}
			MerchAdmin merchAdmin = merchAdminRst.getData();
			roleIdSb.append(merchAdmin.getRoles());
		} else if (null != (coll = principals.fromRealm("shop-center")) && coll.size() > 0) {
			Long shopId = coll.iterator().next();
			ShopAdminId id = new ShopAdminId();
			id.setAdminId(adminId);
			id.setShopId(shopId);
			ShopAdmin4Search shopAdmin4s = new ShopAdmin4Search();
			shopAdmin4s.setId(id);
			ObjectResult<ShopAdmin> shopAdminRst = shopAdminFeign.one(new Body<ShopAdmin4Search, Sorting>(shopAdmin4s, new Sorting()));
			if (shopAdminRst.getStatus() != Status.OK) {
				throw new AuthenticationException(shopAdminRst.getMessage());
			}
			ShopAdmin shopAdmin = shopAdminRst.getData();
			roleIdSb.append(shopAdmin.getRoles());
		}
		return stringToLongArray(roleIdSb.toString());
	}
	
	private Long[] stringToLongArray(String str) {
		String[] strIds = str.split("\\,");
		List<Long> ids = new ArrayList<Long>();
		for (String strId: strIds) {
			try {
				ids.add(Long.parseLong(strId));
			} catch (NumberFormatException e) {
			}
		}
		return ids.toArray(new Long[ids.size()]);
	}
	
	private SimplePrincipalCollection createPrincipals(SysAdmin admin) {
		SimplePrincipalCollection principals = new SimplePrincipalCollection();
		principals.add(admin.getAdminId(), "permit-center");
		
		if ((admin.getPlatforms()&(1<<SysPlatform.AUTH.ordinal())) > 0) {
			
		} else if ((admin.getPlatforms()&(1<<SysPlatform.COMPANY.ordinal())) > 0) {
			
		} else if ((admin.getPlatforms()&(1<<SysPlatform.AGENT.ordinal())) > 0) {
			AgentAdmin4Search agentAdmin4s = new AgentAdmin4Search();
			agentAdmin4s.setAdmin(admin);
			ObjectResult<AgentAdmin> agentAdminRst = agentAdminFeign.one(new Body<AgentAdmin4Search, Sorting>(agentAdmin4s, new Sorting()));
			if (agentAdminRst.getStatus() != Status.OK) {
				throw new AuthenticationException(agentAdminRst.getMessage());
			}
			AgentAdmin agentAdmin = agentAdminRst.getData();
			if (null != agentAdmin) {
				principals.add(agentAdmin.getId().getAgentId(), "agent-center");
			}
		} else if ((admin.getPlatforms()&(1<<SysPlatform.MERCH.ordinal())) > 0) {
			MerchAdmin4Search merchAdmin4s = new MerchAdmin4Search();
			merchAdmin4s.setAdmin(admin);
			ObjectResult<MerchAdmin> merchAdminRst = merchAdminFeign.one(new Body<MerchAdmin4Search, Sorting>(merchAdmin4s, new Sorting()));
			if (merchAdminRst.getStatus() != Status.OK) {
				throw new AuthenticationException(merchAdminRst.getMessage());
			}
			MerchAdmin merchAdmin = merchAdminRst.getData();
			if (null != merchAdmin) {
				principals.add(merchAdmin.getId().getMerchId(), "merch-center");
			}
		} else if ((admin.getPlatforms()&(1<<SysPlatform.SHOP.ordinal())) > 0) {
			ShopAdmin4Search shopAdmin4s = new ShopAdmin4Search();
			shopAdmin4s.setAdmin(admin);
			ObjectResult<ShopAdmin> shopAdminRst = shopAdminFeign.one(new Body<ShopAdmin4Search, Sorting>(shopAdmin4s, new Sorting()));
			if (shopAdminRst.getStatus() != Status.OK) {
				throw new AuthenticationException(shopAdminRst.getMessage());
			}
			ShopAdmin shopAdmin = shopAdminRst.getData();
			if (null != shopAdmin) {
				principals.add(shopAdmin.getId().getShopId(), "shop-center");
			}
		}
		
		return principals;
	}

}
